package com.learn.service.impl;

import cn.hutool.core.date.DateUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.learn.base.BusinessResultUtil;
import com.learn.base.PasswordUtil;
import com.learn.base.R;
import com.learn.config.JwtConfig;
import com.learn.dto.system.base.*;
import com.learn.dto.system.resp.SelectUserInformationResp;
import com.learn.dto.system.resp.SysLoginResp;
import com.learn.entity.system.SysOperator;
import com.learn.repository.system.SysOperatorRepository;
import com.learn.service.SysLoginService;
import com.learn.service.SysRestAuthService;
import com.learn.utils.OperatorTokenUtils;
import com.learn.utils.RedisUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.List;
import java.util.concurrent.TimeUnit;

import static com.learn.emuns.LearnResultEnums.NOT_OPERATOR;

/**
 * 系统管理员登录的业务处理实现类
 *
 * @author yaoqihui
 * @version 2025/3/26
 * @see [相关类/方法]
 * @since [learn-platform]
 */
@Service
@Slf4j
@RequiredArgsConstructor
public class SysLoginServiceImpl implements SysLoginService
{

	private final SysOperatorRepository sysOperatorRepository;
	private final SysRestAuthService sysRestAuthService;
	private final JwtConfig jwtConfig;
	private final RedisUtils redisUtils;

	/**
	 * 操作员登录
	 *
	 * @param loginAccount 账号
	 * @param password     密码
	 * @param channel      渠道
	 */
	@Override
	public R<SysLoginResp> loginPassword (String loginAccount, String password, String channel)
	{
		QueryWrapper<SysOperator> sysOperatorQueryWrapper = new QueryWrapper<> ();
		sysOperatorQueryWrapper.lambda ().eq (SysOperator::getLoginAccount, loginAccount);
		SysOperator sysOperator = sysOperatorRepository.selectOne (sysOperatorQueryWrapper);
		if (sysOperator == null)
		{
			return BusinessResultUtil.build (NOT_OPERATOR.getStatus (), NOT_OPERATOR.getErrMsg (), null);
		}

		//校验账号的密码是否正确
		boolean verifyPassword = PasswordUtil.verifyPassword (password, sysOperator.getPassword ());
		if (! verifyPassword)
		{
			return BusinessResultUtil.build (NOT_OPERATOR.getStatus (), NOT_OPERATOR.getErrMsg (), null);
		}

		//登录成功处理
		List<String> restAuthList = sysRestAuthService.queryRestAuths (sysOperator.getId ());

		// 生成Token信息
		Token token = this.createOperatorToken (sysOperator.getId (), sysOperator.getLoginAccount (),
				sysOperator.getLoginType (), sysOperator.getTenantId (), channel, restAuthList);

		//响应登录
		Operator operator = new Operator ();
		operator.setId (sysOperator.getId ());
		operator.setLoginAccount (sysOperator.getLoginAccount ());

		SysLoginResp sysLoginResp = new SysLoginResp ();
		sysLoginResp.setToken (token);
		sysLoginResp.setOperator (operator);
		return BusinessResultUtil.buildSuccess (sysLoginResp);
	}

	/**
	 * 查询用户信息
	 *
	 * @param operatorId 用户id
	 */
	@Override
	public R<SelectUserInformationResp> selectUserInformation (String operatorId)
	{
		SysOperator sysOperator = sysOperatorRepository.selectById (operatorId);
		SelectUserInformationResp selectUserInformationResp=new SelectUserInformationResp ();
		//selectUserInformationResp.setOperator (sysOperator);
		return BusinessResultUtil.buildSuccess (null);
	}

	private Token createOperatorToken (String operatorId, String loginAccount, String loginType, String tenantId,
			String channel, List<String> restAuthList)
	{
		CreateTokenReq createTokenReq = new CreateTokenReq ().setOperatorId (operatorId).setAccount (loginAccount)
				.setAccountType (loginType).setTenantId (tenantId).setChannel (channel).setRestAuthList (restAuthList);
		return startCreateOperatorToken (createTokenReq);
	}

	public Token startCreateOperatorToken (CreateTokenReq createTokenReq)
	{
		// 总结：生成JWT，并保存相关信息到redis

		JwtConfig.TokenConfig adminTokenConfig = jwtConfig.getAdminConfig ();

		// 相关参数预生成
		String channel = createTokenReq.getChannel ();
		String operatorId = createTokenReq.getOperatorId ();
		String account = createTokenReq.getAccount ();
		String tenantId = createTokenReq.getTenantId ();
		String ua = createTokenReq.getUa ();
		String clientIp = createTokenReq.getClientIp ();
		List<String> restAuthList = createTokenReq.getRestAuthList ();

		// 未携带渠道参数，使用默认配置值
		if (StringUtils.isEmpty (channel))
		{
			channel = adminTokenConfig.getDefaultChannel ();
		}

		// 当前时间
		LocalDateTime now = LocalDateTime.now (ZoneOffset.UTC);
		long currentTime = DateUtil.currentSeconds ();
		// accessToken过期时间
		long accessTokenExpireTime = currentTime + adminTokenConfig.getAccessTokenExpires ();
		// refreshToken过期时间
		long refreshTokenExpireTime = currentTime + adminTokenConfig.getRefreshTokenExpires ();

		// 生成accessToken
		String accessTokenOriToken = OperatorTokenUtils.getOriToken ();
		OperatorTokenUtils.TokenClaim accessTokenClaim = new OperatorTokenUtils.TokenClaim ().setSub (operatorId)
				.setOriToken (accessTokenOriToken).setOperatorId (operatorId).setAccount (account)
				.setTenantId (tenantId).setExp (accessTokenExpireTime).setChannel (channel);
		String accessToken = OperatorTokenUtils.createOperatorToken (accessTokenClaim, jwtConfig.getAccountSecretKey (),
				jwtConfig.getKey ());

		// 生成refreshToken
		String refreshTokenOriToken = OperatorTokenUtils.getOriToken ();
		OperatorTokenUtils.TokenClaim refreshTokenClaim = new OperatorTokenUtils.TokenClaim ().setSub (operatorId)
				.setOriToken (refreshTokenOriToken).setOperatorId (operatorId).setAccount (account)
				.setTenantId (tenantId).setExp (refreshTokenExpireTime).setChannel (channel);
		String refreshToken = OperatorTokenUtils.createOperatorToken (refreshTokenClaim,
				jwtConfig.getAccountSecretKey (), jwtConfig.getKey ());

		// 根据配置构造各自的key
		String accessTokenKey = String.format (adminTokenConfig.getAccessTokenKeyFormat (), operatorId, channel);
		String refreshTokenKey = String.format (adminTokenConfig.getRefreshTokenKeyFormat (), operatorId, channel);
		String operatorStatusKey = String.format (adminTokenConfig.getUserStatusTokenKeyFormat (), operatorId, channel);

		// 保存到redis
		// value= CacheTokenInfo实例
		redisUtils.set (accessTokenKey,
				new CacheTokenInfo ().setId (operatorId).setAccount (account).setTenantId (tenantId).setUa (ua)
						.setClientIp (clientIp).setClientType (channel).setRestAuthList (restAuthList)
						.setRefreshTokenKey (refreshTokenKey).setStatusKey (operatorStatusKey)
						.setOriginToken (accessTokenOriToken), adminTokenConfig.getAccessTokenExpires (),
				TimeUnit.SECONDS);
		// 保存到redis
		// value= CacheRefreshTokenInfo实例
		redisUtils.set (refreshTokenKey,
				new CacheRefreshTokenInfo ().setAccessTokenKey (accessTokenKey).setStatusKey (operatorStatusKey)
						.setClientType (channel).setOriginToken (refreshTokenOriToken),
				adminTokenConfig.getRefreshTokenExpires (), TimeUnit.SECONDS);

		redisUtils.set (operatorStatusKey,
				new CacheUserStatus ().setAccessTokenKey (accessTokenKey).setRefreshTokenKey (refreshTokenKey),
				adminTokenConfig.getAccessTokenExpires (), TimeUnit.SECONDS);
		return new Token ().setAccessToken (accessToken)
				.setExpireTime (now.plusSeconds (adminTokenConfig.getAccessTokenExpires ())).setAccount (account)
				.setCreateTime (now).setExpires (adminTokenConfig.getAccessTokenExpires ())
				.setApplyType (adminTokenConfig.getApplyType ()).setLoginAccount (account)
				.setRefreshToken (refreshToken).setChannel (channel);
	}
}